package com.yupi.yurpc;

import com.yupi.yurpc.config.RegistryConfig;
import com.yupi.yurpc.config.RpcConfig;
import com.yupi.yurpc.constant.RpcConstant;
import com.yupi.yurpc.registry.Registry;
import com.yupi.yurpc.registry.RegistryFactory;
import com.yupi.yurpc.utils.ConfigUtils;
import lombok.extern.slf4j.Slf4j;

/**
 * @program: yu-rpc
 * @description: Java Lotus
 * @author: spring
 * @create: 2024-10-23
 */
/*
RPC 框架中需要维护一个全局的配置对象。在引入 RPC 框架的项目启动时，
从配置文件中读取配置并创建对象实例，之后就可以集中地从这个对象中获取配置信息，
而不用每次加载配置时再重新读取配置、并创建新的对象，减少了性能开销。
使用设计模式中的 单例模式，就能够很轻松地实现这个需求了。
 */
@Slf4j
public class RpcApplication {

    //双检锁单例模式 的经典实现，支持在获取配置时才调用 init 方法实现懒加载。

    /**
     * RPC 框架应用
     * 相当于 holder，存放了项目全局用到的变量。双检锁单例模式实现
     */
    //volatile 是一个关键字，主要用于告诉编译器某个变量可能会被异步地改变，
    // 通常是由其他线程或硬件引起的。使用 volatile 变量可以避免编译器的优化，
    // 使得每次访问该变量时都从内存中读取，而不是使用缓存的值。

    private static volatile RpcConfig rpcConfig;

    //为了便于扩展，还支持自己传入配置对象；如果不传入，则默认调用前面写好的 ConfigUtils 来加载配置。

    /**
     * 框架初始化，支持传入自定义配置
     * 框架初始化，支持传入自定义配置
     * @param newRpcConfig 传入的新的参数
     */
    public static void init(RpcConfig newRpcConfig) {
        rpcConfig = newRpcConfig;
        log.info("rpc init, config = {}", newRpcConfig.toString());
        // 注册中心初始化
        RegistryConfig registryConfig = rpcConfig.getRegistryConfig();
        Registry registry = RegistryFactory.getInstance(registryConfig.getRegistry());
        registry.init(registryConfig);
        log.info("registry init, config = {}", registryConfig);

        //创建并且注册 Shutdown Hook ， JVM 退出的时候执行操作
        Runtime.getRuntime().addShutdownHook(new Thread(registry::destroy));
    }



    /**
     * 初始化
     */
    public static void init() {
        RpcConfig newRpcConfig;
        try {
            newRpcConfig = ConfigUtils.loadConfig(RpcConfig.class, RpcConstant.DEFAULT_CONFIG_PREFIX);
        } catch (Exception e) {
            //配置加载失败，使用默认的初始值
            newRpcConfig = new RpcConfig();
        }
        init(newRpcConfig);
    }


    /**
     * 获取配置
     * @return 返回rpc参数
     */
    public static RpcConfig getRpcConfig() {
        if (rpcConfig == null) {
            synchronized (RpcApplication.class) {
                if (rpcConfig == null) {
                    init();
                }
            }
        }
        return rpcConfig;
    }

}
